using System;
using Microsoft.Xna.Framework;

namespace Oops.Xna.Framework.Viewpoint.Cameras
{
	/// <summary>
	/// Represents a camera that looks at and follows a dynamic target.
	/// </summary>
	public class LookAtCamera : Camera 
	{
		/// <summary>
		/// Gets or sets the the orientation of <see cref="LookAtCamera"/> instance's up vector.
		/// </summary>
		public Vector3 CameraUp = Vector3.Up;

		private ICameraCallback callback;

		/// <summary>
		/// Initializes a new instance of the <see cref="LookAtCamera"/> class.
		/// </summary>
		/// <param name="game">The associated <see cref="Microsoft.Xna.Framework.Game"/>.</param>
		/// <param name="name">The name of the <see cref="LookAtCamera"/>.</param>
		/// <param name="callback">The <see cref="ICameraCallback"/> used to determine the dynamic target's position.</param>
		public LookAtCamera(Game game, string name, ICameraCallback callback) : base(game, name)
		{
			// The callback parameter must exist.
			if (callback == null)
			{
				throw new ArgumentNullException("callback");
			}

			// Hold onto parameters.
			this.callback = callback;
		}

		#region IUpdateable Members

		public override void Update(GameTime gameTime)
		{
			// Get the target position.
			Vector3 position = Vector3.Zero;
			Matrix orientation = Matrix.Identity;
			this.callback.GetTransform(ref position, ref orientation);

			// Adjust the orientation so it's looking at the target.
			this.Orientation.Forward = Vector3.Normalize(position - this.Position);
			this.Orientation.Right = Vector3.Normalize(Vector3.Cross(this.Orientation.Forward, this.CameraUp));
			this.Orientation.Up = Vector3.Cross(this.Orientation.Right, this.Orientation.Forward);
			
			// Call inherited method.
			base.Update(gameTime);
		}

		#endregion
	}
}
